<template>
  <div class="container" v-loading="loading">
    <h2>根据输入条件生成随机数并导出到excel</h2>
    <el-form
      class="demo-ruleForm"
      ref="ruleFormRef"
      :model="ruleForm"
      :rules="rules"
      label-width="120px"
      status-icon
    >
      <el-form-item label="数据行数：" prop="row">
        <el-input v-model="ruleForm.row" placeholder="请输入数字，例如：100"></el-input>
      </el-form-item>
      <el-form-item label="数据列数：" prop="cell">
        <el-input v-model="ruleForm.cell" placeholder="请输入数字，例如：11"></el-input>
      </el-form-item>
      <el-form-item label="数据范围：" prop="range">
        <el-input
          v-model="ruleForm.range"
          placeholder="请输入范围，例如：[[47,50],[48,51],[49,52],[50,53],[51,54],[52,55],...]"
        ></el-input>
      </el-form-item>
      <el-form-item>
        <el-button type="primary" @click="submitForm()">生成</el-button>
        <el-button @click="resetForm()">重置</el-button>
      </el-form-item>
    </el-form>
  </div>
</template>

<script lang="ts" setup>
import { getCurrentInstance, reactive, ref } from 'vue'
import { ElMessage } from 'element-plus'

const ruleFormRef = ref()
let ruleForm = reactive({
  row: '100',
  range: '[[47,50],[48,51],[49,52],[50,53],[51,54],[52,55]]',
  cell: '11',
})
let rules = reactive({
  row: [{ required: true, message: '请输入数据行数！', trigger: 'change' }],
  range: [{ required: true, message: '请输入数据范围！', trigger: 'change' }],
  cell: [{ required: true, message: '请输入数据列数！', trigger: 'change' }],
})

// 存储生成的随机数
let resArr = reactive<RecordRaw[]>([])
let loading = ref(false)

type Range = [number, number]

/**
 * 获取最大值和最小值
 * @param range
 * @returns [min,max]
 */
const getRangeGroup = (range: Range[]) => {
  let index = Number((Math.random() * range.length).toFixed(0))
  if (!range[index]) return getRangeGroup(range)
  return range[index]
}

/**
 * 生成随机数
 * @param range [[47,50],[48,51],[49,52],[50,53],[51,54],[52,55]]
 * @param row
 * @param cell
 * @param min
 * @param max
 * @param rowObj
 */
const generateRandom = (
  range: Range[],
  row: number,
  cell: number,
  min: number,
  max: number,
  rowObj: RecordRaw,
) => {
  // 如果行数已经达到预定的行数row
  if (resArr.length >= row) return

  // 如果当前行数据已经达到预定的列数cell，则重新获取最大值和最小值
  if (Object.keys(rowObj).length === cell) {
    const [newMin, newMax] = getRangeGroup(range)
    min = newMin
    max = newMax
    rowObj = { 范围: `${min},${max}` }
  }

  rowObj[Object.keys(rowObj).length] = Number((Math.random() * (max - min) + min).toFixed(1))
  if (Object.keys(rowObj).length === cell) {
    resArr.push(rowObj)
  }

  generateRandom(range, row, cell, min, max, rowObj)
}

/**
 * 提交表单
 */
const submitForm = () => {
  ruleFormRef.value.validate((valid: any) => {
    if (!valid) {
      return false
    }

    const row = Number(ruleForm.row) // 要生成的数字行数
    const cell = Number(ruleForm.cell) // 每行的数字个数
    const range = JSON.parse(ruleForm.range) // 最大值和最小值的范围

    try {
      resArr = []
      const [min, max] = getRangeGroup(range)
      generateRandom(range, row, cell, min, max, { 范围: `${min},${max}` })
      exportExcel(resArr)
    } catch (e) {
      ElMessage.error('程序报错，请重新生成！')
      console.log(e)
    }
  })
}

/**
 * 重置表单
 */
const resetForm = () => {
  ruleFormRef.value.resetFields()
}

/**
 * 导出excel
 * @param list
 */
const exportExcel = (list: Range[]) => {
  loading.value = true
  const SheetJSWebWorker = new Worker(
    URL.createObjectURL(
      new Blob([
        `
        importScripts("https://cdn.sheetjs.com/xlsx-latest/package/dist/xlsx.full.min.js");
        onmessage = ({ data }) => {
          const wb = XLSX.utils.book_new();
          const ws = XLSX.utils.json_to_sheet(data, { dense: true });
          XLSX.utils.book_append_sheet(wb, ws);
          postMessage(XLSX.write(wb, { type: "array", bookType: "xlsx", bookSST: true, compression: true }))
        };
    `,
      ]),
    ),
  )

  SheetJSWebWorker.postMessage(list)
  SheetJSWebWorker.onmessage = ({ data }) => {
    const a = document.createElement('a')
    a.download = `${new Date().toLocaleTimeString()}.xlsx`
    a.href = URL.createObjectURL(new Blob([data], { type: 'application/octet-stream' }))
    a.click()
    loading.value = false
  }
}
</script>

<style scoped lang="less">
.container {
  h2 {
    text-align: left;
    margin-bottom: 20px;
  }
}
</style>
